package com.wang.socket;


import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer;
import com.wang.conf.AppData;
import com.wang.packpojo.ConMsg;
import com.wang.pojo.Device;
import com.wang.util.*;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.UnsupportedEncodingException;
import java.net.InetSocketAddress;
import java.util.Arrays;
import java.util.List;

/**
 * 自定义解码器
 *
 * @author wcy   添加功能解析功能
 */
public class CustomDecoder12 extends ByteToMessageDecoder {

    private final int BASE_LENGTH = 1;

    private String heart = "METER";
    private String reg = "DSDEVREG";
    Integer jNum = 0;  //垃圾数据次数
    Integer m1Num = 0;  //垃圾数据次数
    Integer m2Num = 0;  //垃圾数据次数
    Integer m3Num = 0;  //垃圾数据次数
    Integer mCountNum = 0;  //控制垃圾数据次数

    /**
     * 十六进制字符串数组解析
     *
     * @param ctx
     * @param in   传入数据
     * @param list 添加解码消息
     * @throws Exception
     */
    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> list) {


        Logger logger = LoggerFactory.getLogger(PrintUtil.class);

        int beginIndex;//记录包开始位置

        while (true) {
            //获取包头开始的index
            beginIndex = in.readerIndex();
            // 将当前的readerIndex备份到markedReaderIndex中
            in.markReaderIndex();
            //获取字节可读取长度
            int reablesLength = in.readableBytes();

            byte[] bytetest = new byte[reablesLength];
            in.readBytes(bytetest);
            //将数据转换成16进制字符串
            String test = new String(bytetest);
            PrintUtil.print("测试数据："+test);
            in.readerIndex(beginIndex);

            //1、判断是不是注册包
            //--------------------------------------------

            //注册包处理
            if(reablesLength >= reg.getBytes().length){
                //读取前8位字节 判断是不是注册包数据
                byte[] bytes2 = new byte[reg.getBytes().length];
                in.readBytes(bytes2);

                String encoding1 = GetByteEncodeUtil.getEncoding(bytes2);
                String data2 = null;
                try {
                    data2 = new String(bytes2, encoding1);
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
                if (data2.equals(reg)) {
                    //继续往下读  读到；结束
                    //定义下标
                    int index = 0;
                    //定义包含;的标志
                    boolean containFlag = false;

                    for (int i = 0; i < reablesLength - reg.getBytes().length; i++) {

                        //读取一个字节
                        byte[] bytesFlag = new byte[1];
                        in.readBytes(bytesFlag);

                        String encoding2 = GetByteEncodeUtil.getEncoding(bytesFlag);
                        String flag = null;
                        try {
                            flag = new String(bytesFlag, encoding2);
                        } catch (UnsupportedEncodingException e) {
                            e.printStackTrace();
                        }

                        if (flag.equals(";")) {
                            index = i;
                            containFlag = true;
                            break;
                        }
                    }
                    if (containFlag) {
                        //重置读取字节位置
                        in.resetReaderIndex();
                        //读取完整的注册包数据
                        byte[] regDatas = new byte[reg.getBytes().length + index + 1];
                        in.readBytes(regDatas);

                        String encoding3 = GetByteEncodeUtil.getEncoding(regDatas);
                        String ctxData = null;
                        try {
                            ctxData = new String(regDatas, encoding3);
                        } catch (UnsupportedEncodingException e) {
                            e.printStackTrace();
                        }
                        
                        //添加数据
                        list.add(ctxData.replace(";", ""));
                        //回收已读字节
                        in.discardReadBytes();
                        return;
                    }else {
                        //重置读取点
                        in.readerIndex(beginIndex);
                        return;
                    }
                }else {
                    //重置读取点
                    in.readerIndex(beginIndex);

                }

            }else {
                in.clear();
                return;
            }



            //2、 判断心跳包 先判断全局变量已注册的是否包含此设备
            String address = getRemoteAddress(ctx);



            String isContain = "false";
            if (SocketMap.mRegPassDeviceMap.containsKey(address)) {
                isContain = "mContain";

                byte[] bytes = new byte[reablesLength];
                in.readBytes(bytes);

                try {
                    ConMsg conMsg = SocketMap.mRegPassDeviceMap.get(address);
                    String data = ModBusUtil.bytes2HexString(bytes);
                    PrintUtil.print(conMsg.getDevice().getDevGId()+"MODBUS打印调试数据："+data);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                //重置读取点
                in.readerIndex(beginIndex);

            }


            if (SocketMap.jRegPassDeviceMap.containsKey(address)) {

                byte[] bytes = new byte[reablesLength];
                in.readBytes(bytes);

                String encoding4 = GetByteEncodeUtil.getEncoding(bytes);
                String data = null;
                try {

                    ConMsg conMsg = SocketMap.jRegPassDeviceMap.get(address);
                    data = new String(bytes, encoding4);
                    System.out.println(conMsg.getDevice().getDevGId()+"JSON打印调试数据："+data);
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
                //重置读取点
                in.readerIndex(beginIndex);

                isContain = "jContain";
            }


            if (SocketMap.c1RegPassDeviceMap.containsKey(address)) {
                isContain = "c1Contain";

                byte[] bytes = new byte[reablesLength];
                in.readBytes(bytes);

                try {
                    ConMsg conMsg = SocketMap.c1RegPassDeviceMap.get(address);
                    String data = ModBusUtil.bytes2HexString(bytes);
                    System.out.println(conMsg.getDevice().getDevGId()+"CustomOne打印调试数据："+data);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                //重置读取点
                in.readerIndex(beginIndex);

            }


            if (isContain.equals("false")) {
                in.clear();
                logger.info(address+"设备未注册，收到了数据");
                return;
            }
            //---------------------------------
            //2、判断是不是心跳包
            if (!isContain.equals("false")) {

                if (reablesLength >= heart.getBytes().length) {
                    //先读前心跳包长度 判断是是不是心跳包数据
                    byte[] bytes = new byte[heart.getBytes().length];
                    in.readBytes(bytes);

                    String encoding4 = GetByteEncodeUtil.getEncoding(bytes);
                    String data = null;
                    try {
                        data = new String(bytes, encoding4);
                    } catch (UnsupportedEncodingException e) {
                        e.printStackTrace();
                    }
                    if (data.equals(heart)) {
                        //添加数据
                        list.add(data);
                        //回收已读字节
                        in.discardReadBytes();
                        return;
                    }else {
                        //重置读取点
                        in.readerIndex(beginIndex);
                    }

                }

            }

            //3、判断是不是json数据
            //--------------------------------
            if (isContain.equals("jContain")) {



                //方式  读取左大括号 +1   读取到右括号-1
                //先读取第一个字节 是否是 { 如果是依次读字节
                byte[] byteleft = new byte[1];
                in.readBytes(byteleft);

                String encoding6 = GetByteEncodeUtil.getEncoding(byteleft);
                String firstData = null;
                try {
                    firstData = new String(byteleft, encoding6);
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
                if (firstData.equals("{")) {
                    //下标
                    int indexFlag = 0;
                    int intFlag = 0;


                    indexFlag++;
                    intFlag++;
                    //依次往下读
                    //读取一个字节 -1 代表已经读过一个字节
                    for (int i = 0; i < reablesLength - 1; i++) {

                        byte[] bytesFlag = new byte[1];
                        in.readBytes(bytesFlag);
                        indexFlag++;
                        String encoding7 = GetByteEncodeUtil.getEncoding(bytesFlag);
                        String flag = null;
                        try {
                            flag = new String(bytesFlag, encoding7);
                        } catch (UnsupportedEncodingException e) {
                            e.printStackTrace();
                        }
                        if (flag.equals("{")) {
                            //标识+1
                            intFlag++;
                        }
                        if (flag.equals("}")) {
                            //标识-1
                            intFlag--;
                        }
                        if (intFlag == 0) {
                            //如果标识为0 查询到所有json数据
                            break;
                        }
                    }

                    if (intFlag == 0) {


                        //截取数据
                        //重置字节读取下标
                        in.resetReaderIndex();
                        byte[] jsonBytes = new byte[indexFlag];
                        in.readBytes(jsonBytes);
                        String encoding8 = GetByteEncodeUtil.getEncoding(jsonBytes);
                        logger.info("编码格式:" + encoding8);
                        String jsonData = null;
                        try {
                            jsonData = new String(jsonBytes, encoding8);
                        } catch (UnsupportedEncodingException e) {
                            e.printStackTrace();
                        }

                        //添加数据
                        list.add(jsonData);
                        //回收已读字节
                        in.discardReadBytes();
                        jNum = 0;

                        return;

                    } else {

                        //如果不包含 代表数据不全，返回读的指针 等待下一次的数据到期在读
                        //如果垃圾数据次数不超过5次 重置
                        if(jNum<=10) {

                            System.out.println("打印调试数据："+jNum);
                            in.readerIndex(beginIndex);
                            jNum++;
                        }else {
                            //如果垃圾数据次数超过5次  清空数据
                            in.clear();
                            jNum =0;
                        }
                        return;
                    }


                }else {
                    //清空字节
                    in.clear();
                    //回收已读字节
                    //in.discardReadBytes();
                    return;
                    //如果不包含 代表数据不全，返回读的指针 等待下一次的数据到期在读
                   /* in.readerIndex(beginIndex);
                    return;*/
                }
            }
            //4、判断是不是modbus数据
            if (isContain.equals("mContain")) {
                //最后一步 modbus数据读取


                if (reablesLength >= 5){
                    //截取功能码 判断是不是03 读取数据
                    byte[] funMdatas = new byte[2];
                    in.readBytes(funMdatas);
                    String funCode = bytes2HexString(funMdatas).split(" ")[1];

                    //logger.info("地址位"+addressCode+"功能码"+funCode);

                    if (funCode.equals("03") || funCode.equals("04")) {  //如果是读取数据03  04也按读取数据处理

                        //重置读取位
                        in.resetReaderIndex();

                        //modbus数据读取前3个字节 获取数据长度
                        byte[] mdatas = new byte[3];
                        in.readBytes(mdatas);
                        //截取数据位长度
                        String mlength = bytes2HexString(mdatas).split(" ")[2];


                        //重置读取位
                        in.resetReaderIndex();

                        Integer length = hex2decimal(mlength);

                        int totalDataLength = 3 + length + 2;  //地址 +功能 +数据长度  3   加上数据长度 +  校验位长度2
                        //如果可读数据位长度小于  数据位长度  返回 等待下一次数据到期在读
                        if (reablesLength < totalDataLength) {
                            //in.readerIndex(beginIndex);
                            //清空数据 重新读取
                           /* in.clear();
                            return;*/
                            if(m1Num<=3) {

                                System.out.println("打印调试数据modbus1："+m1Num);
                                in.readerIndex(beginIndex);
                                m1Num++;
                            }else {
                                //如果垃圾数据次数超过5次  清空数据
                                in.clear();
                                m1Num =0;
                            }
                            return;
                        }
                        byte[] mdatas2 = new byte[totalDataLength];

                        in.readBytes(mdatas2);


                        //计算modbus数据合法性
                        //先将bytes 转换成16进制字符串
                        String baseData = ModBusUtil.bytes2HexString(mdatas2);


                        String baseDataStr = baseData.replaceAll(" ", "");
                        String[] baseDatas = baseData.split(" ");

                        //要校验的数据
                        String[] checkDatas = Arrays.copyOfRange(baseDatas, 0, baseDatas.length - 2);
                        StringBuffer sb1 = new StringBuffer();
                        for (String s : checkDatas) {
                            sb1.append(s);
                        }

                        byte[] checkBytes = ModBusUtil.HexString2Bytes(sb1.toString());


                        //计算crc值
                        String checkCrc2 = ModBusUtil.getCRC3(checkBytes);
                        //截取原有crc值
                        String checkCrc1 = baseDataStr.substring(baseDataStr.length() - 4);

                        if (checkCrc1.equals(checkCrc2)) {
                            //添加数据
                            list.add(baseData);

                            //回收已读字节
                            in.discardReadBytes();
                            m2Num = 0;
                            return;

                        } else {
                           /* //回收已读字节
                            in.discardReadBytes();
                            return;*/

                            //如果不包含 代表数据不全，返回读的指针 等待下一次的数据到期在读
                            //如果垃圾数据次数不超过5次 重置
                            if(m2Num<=3) {

                                System.out.println("打印调试数据modbus2："+m2Num);
                                in.readerIndex(beginIndex);
                                m2Num++;
                            }else {
                                //如果垃圾数据次数超过5次  清空数据
                                in.clear();
                                m2Num =0;
                            }
                            return;
                        }
                    } else {
                        //判断设备处理 指令处理
                        ConMsg conMsg =  SocketMap.mRegPassDeviceMap.get(address);
                        Device device = conMsg.getDevice();
                        System.out.println("功能码："+funCode);
                        if(AppData.returnMap.containsKey(device.getDevGId())){
                            //如果功能码是10 例外处理 目前先作为电极校准处理

                            if(funCode.equals("10")){
                                System.out.println("测试1");
                                DevControlUtil.handerControl2(in, device.getDevGId(), mCountNum);
                            }else {
                                //通用性 单独处理
                                DevControlUtil.handerControl1(in, device.getDevGId(), mCountNum);
                            }
                        }else{
                            //清空垃圾数据
                            in.clear();
                        }
                        return;

                    }

                }else {
                    in.clear();
                    logger.info("清空缓冲区：字节数小于5，不符合数据");
                    return;
                }

            }

            //5、判断是不是customOne数据
            if (isContain.equals("c1Contain")) {
                //最后一步 customone数据读取

                if (reablesLength >= 5){
                    //截取功能码 判断是不是03 读取数据
                    byte[] funMdatas = new byte[2];
                    in.readBytes(funMdatas);
                    String headerCode = bytes2HexString(funMdatas).replace(" ","");



                    if (headerCode.equals("AA77")) {  //如果是读取数据



                        //重置读取位
                        in.resetReaderIndex();

                        //modbus数据读取前3个字节 获取数据长度
                        byte[] c1Datas = new byte[4];
                        in.readBytes(c1Datas);
                        //截取数据位长度
                        String[] datas = bytes2HexString(c1Datas).split(" ");
                        //取第三个字节和第四个字节
                        String c1lengthHex =  datas[2]+datas[3];
                        Integer length = hex2decimal(c1lengthHex);

                        int totalDataLength = 2+ length;  //header 2位 +长度
                        //重置读取位
                        in.resetReaderIndex();


                        //如果可读数据位长度小于  数据位长度  返回 等待下一次数据到期在读
                        if (reablesLength < totalDataLength) {
                            //in.readerIndex(beginIndex);
                            //清空数据 重新读取
                            in.clear();
                            return;
                        }
                        byte[] c1Datas2 = new byte[totalDataLength];

                        in.readBytes(c1Datas2);

                        String baseData = CustomOneUtil.bytes2HexString(c1Datas2);
                        //添加数据
                        list.add(baseData);
                        in.discardReadBytes();

                        return;
                    } else {
                        in.clear();
                        logger.info("清空缓冲区：开头数据不是AA 77");
                        return;
                    }

                }else {
                    in.clear();
                    logger.info("清空缓冲区：字节数小于5，不符合数据");
                    return;
                }

            }

            //最后不符合所有数据条件  断开连接
            in.clear();
            logger.info("清空数据 "+address+"不符合数据条件");
            return;

        }


    }




    //通用控制开关处理器







    /*
     * 字节数组转16进制字符串
     */
    public static String bytes2HexString(byte[] b) {
        String r = "";
        for (int i = 0; i < b.length; i++) {
            String hex = Integer.toHexString(b[i] & 0xFF);
            if (hex.length() == 1) {
                hex = '0' + hex;
            }
            r += hex.toUpperCase() + " ";
        }
        return r;
    }

    // 16进制字符串转换10进制
    public static int hex2decimal(String hex) {
        return Integer.parseInt(hex, 16);
    }

    //获取连接ctx的地址
    public String getRemoteAddress(ChannelHandlerContext ctx) {
        InetSocketAddress ipSocket = (InetSocketAddress) ctx.channel().remoteAddress();
        String clientIp = ipSocket.getAddress().getHostAddress();
        Integer port = ipSocket.getPort();
        String ip = clientIp + ":" + port;
        return ip;
    }


    //异常断开 和正常断开socket  清除连接
    public void disConnectDev(String address) {

        //modbus设备
        if (SocketMap.mRegPassDeviceMap != null && SocketMap.mRegPassDeviceMap.containsKey(address)) {
            ConMsg conMsg = SocketMap.mRegPassDeviceMap.get(address);
            conMsg.getCtx().close();
        }
        //json设备
        if (SocketMap.jRegPassDeviceMap != null && SocketMap.jRegPassDeviceMap.containsKey(address)) {
            ConMsg conMsg = SocketMap.jRegPassDeviceMap.get(address);
            conMsg.getCtx().close();
        }


        if (SocketMap.notRegDeviceMap != null && SocketMap.notRegDeviceMap.containsKey(address)) {
            ConMsg conMsg = SocketMap.notRegDeviceMap.get(address);
            conMsg.getCtx().close();
            SocketMap.notRegDeviceMap.remove(address);
        }
        if (SocketMap.regFailDeviceMap != null && SocketMap.regFailDeviceMap.containsKey(address)) {
            ConMsg conMsg = SocketMap.regFailDeviceMap.get(address);
            conMsg.getCtx().close();
            SocketMap.regFailDeviceMap.remove(address);
        }


    }

}


